<!--Copyright (c) 2012 Ryan Stemen

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.-->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Prototype 003 - Actors</title>
    <script type="text/javascript"><!--
        const WIDTH = 1280;
        const HEIGHT = 720;
        
        var center = new vec2(WIDTH / 2, HEIGHT / 2);
        var context;
        var canvas;
        var bullets = new Array();
        var bulletOrigin = new vec2(center.x, center.y);
        var keyboardVec = new vec2(0, 0);
        var keyboardState = new Object();
        var mousePosition = new vec2(0,0);
        var mouseDown = false;
        var counter = 0.0;
        
        var actors = new Array();
        
        window.onload = init;
        window.onmousemove = onMouseMove;
        window.onmousedown = onMouseDown;
        window.onmouseup = onMouseUp;
        window.onkeydown = onKeyDown;
        window.onkeyup = onKeyUp;
        
        
        function vec2($x, $y){
            this.x = ($x == null) ? 0 : $x;
            this.y = ($y == null) ? 0 : $y;
        }
        
        vec2.prototype.lengthSquared = function(){
            return this.x * this.x + this.y * this.y;
        }
        
        vec2.prototype.length = function(){
            return Math.sqrt(this.x * this.x + this.y * this.y);
        }
        
        vec2.prototype.normalize = function(){
            var length = this.length();
            if(length == 0)
                return new vec2(); 
            return new vec2(this.x / length, this.y / length);
        }
        
        vec2.prototype.plus = function($vec2_2){
            return new vec2(this.x + $vec2_2.x, this.y + $vec2_2.y);
        }
        
        vec2.prototype.minus = function($vec2_2){
            return new vec2(this.x - $vec2_2.x, this.y - $vec2_2.y);
        }
        
        vec2.prototype.times = function($val){
            return new vec2(this.x * $val, this.y * $val);
        }
        
        vec2.prototype.divide = function($val){
            return new vec2(this.x / $val, this.y / $val);
        }
        
        function Actor($x, $y, $velocity, $radius){
            this.pos = new vec2($x, $y);
            this.radius = $radius;
            this.velocity = ($velocity) ? $velocity : new vec2();
            this.heading = ($velocity) ? this.velocity.normalize() : new vec2(1, 0);
            
            this.maxTurnRate = 0.1;
            this.maxSpeed = 6.0;
        }
        
        Actor.prototype.getSteeringForce = function(){
            return new vec2();
        }
        
        Actor.prototype.update = function(){
            var steeringForce = this.getSteeringForce();
            var accel = steeringForce.divide(10);
            this.velocity = this.velocity.plus(accel.times(0.33));
            var speed = this.velocity.length();
            this.velocity = this.velocity.divide(speed / Math.min(speed, this.maxSpeed));
            
            this.pos = this.pos.plus(this.velocity);
            
            this.heading = this.velocity.normalize();
        }
        
        Actor.prototype.seek = function($target){
            var desiredVelocity = $target.minus(this.pos).normalize().times(this.maxSpeed);
            return desiredVelocity.minus(this.velocity);
        }
        
        Actor.prototype.flee = function($actors){
             var count = $actors.length;
            var desiredVelocity = new vec2(0, 0);
            for(var i = 0; i < count; i++){
                var a = $actors[i];
                if(a == this)
                    continue;
                var delta = this.pos.minus(a.pos);
                var dist = delta.lengthSquared();
                if(dist > 10000)
                    continue;
                desiredVelocity = desiredVelocity.plus( delta.divide(dist));
            }
            desiredVelocity = desiredVelocity.normalize().times(this.maxSpeed);
            return desiredVelocity.minus(this.velocity);
        }
        
        Actor.prototype.draw = function(){
            context.beginPath();
            context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI*2, true);
            context.stroke();
        }
        
        EnemyA.prototype = new Actor();
        EnemyA.prototype.constructor = EnemyA;
        function EnemyA($x, $y){
            Actor.call(this, $x, $y, new vec2(1,0), 8);
        }
        
        EnemyA.prototype.getSteeringForce = function(){
            return this.seek(bulletOrigin).plus(this.flee(bullets)).plus(this.flee(actors).times(0.4));
        }
        
        Bullet.prototype = new Actor();
        Bullet.prototype.constructor = Bullet;
        function Bullet($x, $y, $velocity){
            Actor.call(this, $x, $y, $velocity, 5);
        }
        
        Bullet.prototype.isOffScreen = function(){
            // if any of these conditionals are true then the bullet can't be seen.
            return (this.pos.x + this.radius < 0)
               || (this.pos.x - this.radius > WIDTH)
               || (this.pos.y + this.radius < 0)
               || (this.pos.y - this.radius > HEIGHT);
        }
        
        function init(){            
            canvas = document.getElementById('c');
            context = canvas.getContext('2d');
            
            
            var loc = new vec2(0, 0);
            for(var i = 0; i < 10; i++){
                do{
                    loc.x = Math.random() * WIDTH;
                    loc.y = Math.random() * HEIGHT;
                }while(loc.minus(bulletOrigin).lengthSquared() < 30000)
                
                actors.push(new EnemyA(loc.x, loc.y));
            }
            
            runLoop();
        }
        
        function onMouseMove(event){
            mousePosition.x = event.pageX;
            mousePosition.y = event.pageY;
        }
        
        function onMouseDown(event){
            mouseDown = true;
        }
        
        function onMouseUp(event){
            mouseDown = false;
        }
        
        function onKeyDown(event){
            var key = String.fromCharCode(event.which).toUpperCase();
            keyboardState[key] = true;
            updateKeyboardVec();
        }
        
        function onKeyUp(event){
            var key = String.fromCharCode(event.which).toUpperCase();
            keyboardState[key] = false;
            updateKeyboardVec();
        }
        
        function updateKeyboardVec(){
            keyboardVec = new vec2(0, 0);
            if(keyboardState['W'])
                keyboardVec.y -= 1;
            if(keyboardState['A'])
                keyboardVec.x -= 1;
            if(keyboardState['S'])
                keyboardVec.y += 1;
            if(keyboardState['D'])
                keyboardVec.x += 1;
                
            keyboardVec = keyboardVec.normalize().times(3);
        }
        
        function runLoop(){
            //remove everything that we drew out previous frame from the screen
            context.clearRect(0, 0, WIDTH, HEIGHT);
            bulletOrigin = bulletOrigin.plus(keyboardVec);
            if(mouseDown && counter % 10 == 0){
                var mouseDelta = mousePosition.minus(bulletOrigin);
                var bulletVelocity = mouseDelta.normalize().times(6).plus(keyboardVec);
                
                var bullet = new Bullet(bulletOrigin.x, bulletOrigin.y, bulletVelocity);            
                bullets.push(bullet);
            }
            
            for(var i = 0; i < bullets.length; i++){
                var b = bullets[i];
                b.update();
                
                if(b.isOffScreen())
                    bullets.splice(i, 1);
                
                b.draw();
            }
            
            for(var i = 0; i < actors.length; i++){
                var a = actors[i];
                a.update();
                a.draw();
            }
            
            //draw the bullet origin
            context.beginPath();
            context.arc(bulletOrigin.x, bulletOrigin.y, 12, 0, Math.PI*2, true);
            context.stroke();
            
            // increment the counter and call the runLoop function after a 14 ms delay
            counter += 1;
            window.setTimeout(runLoop, 14);
        }
        
        
    </script>
</head>
<body>
    <canvas id="c" width="1280px" height="720px">Canvas not supported!</canvas>
</body>
</html>
